﻿namespace AzureLogProviders.Providers.EnterpriseLibrary
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.Reflection;

    using AzureLogProviders.Common;

    public class EnterpriseTracer : ITracer
    {
        #region Fields

        private const string TraceEndMessage = "Tracing Ended For {0}";
        private const string TraceStartMessage = "Tracing Started For {0}";

        private readonly AzureLogProvider logProvider;
        private Stopwatch stopwatch;
        private string traceActivity;
        private TraceMode traceMode;

        #endregion Fields

        #region Constructors

        public EnterpriseTracer(bool isTracingEnabled, AzureLogProvider provider)
        {
            this.IsTracingEnabled = isTracingEnabled;
            this.logProvider = provider;
        }

        #endregion Constructors

        #region Enumerations

        private enum TraceMode
        {
            None,
            Activity,
            Method,
        }

        #endregion Enumerations

        #region Properties

        public bool IsTracingEnabled
        {
            get;
            private set;
        }

        #endregion Properties

        #region Methods

        public void EndTracing()
        {
            if (this.IsTracingEnabled)
            {
                if (this.traceMode == TraceMode.None)
                {
                    throw new Exception("Tracing has not been started.");
                }

                Dictionary<string, object> extendedProp = new Dictionary<string, object>();
                long tracingEndTicks = Stopwatch.GetTimestamp();
                decimal secondsElapsed = this.stopwatch.ElapsedMilliseconds;
                this.stopwatch.Stop();

                string message = this.traceMode == TraceMode.Method ? string.Format(CultureInfo.CurrentCulture, TraceEndMessage, " Method " + this.traceActivity) : string.Format(CultureInfo.CurrentCulture, TraceEndMessage, " Activity " + this.traceActivity);

                extendedProp.Add(Constants.Others.Ticks, tracingEndTicks);

                extendedProp.Add(Constants.Others.MiliSecondElapsed, secondsElapsed);

                this.logProvider.Write(message, LogSeverityLevel.Debug, null, null, extendedProp);
                this.traceMode = TraceMode.None;
                this.traceActivity = string.Empty;
            }
        }

        public bool StartActivityTracing(string activity)
        {
            if (this.IsTracingEnabled)
            {
                if (this.traceMode != TraceMode.None)
                {
                    throw new Exception("Tracing has already been started.");
                }

                this.traceMode = TraceMode.Activity;
                this.traceActivity = activity;
                this.stopwatch = Stopwatch.StartNew();
                long tracingStartTicks = Stopwatch.GetTimestamp();

                string message = string.Format(
                    CultureInfo.CurrentCulture, TraceStartMessage, " Activity " + this.traceActivity);
                Dictionary<string, object> extendedProp = new Dictionary<string, object>
                    {
                        { Constants.Others.Ticks, tracingStartTicks } 
                    };

                this.logProvider.Write(message, LogSeverityLevel.Informational, null, null, extendedProp);
                return true;
            }

            return false;
        }

        public bool StartMethodTracing()
        {
            if (this.IsTracingEnabled)
            {
                this.traceMode = TraceMode.Method;
                this.traceActivity = this.GetExecutingMethodName();
                this.stopwatch = Stopwatch.StartNew();
                long tracingStartTicks = Stopwatch.GetTimestamp();

                string message = string.Format(
                    CultureInfo.CurrentCulture, TraceStartMessage, " Method " + this.GetExecutingMethodName());
                Dictionary<string, object> extendedProp = new Dictionary<string, object>
                    {
                        { Constants.Others.Ticks, tracingStartTicks } 
                    };

                this.logProvider.Write(message, LogSeverityLevel.Debug, null, null, extendedProp);
                return true;
            }

            return false;
        }

        /// <summary>
        /// Gets the name of the executing method.
        /// </summary>
        /// <returns>the method name</returns>
        private string GetExecutingMethodName()
        {
            StackTrace stackTrace = new StackTrace();
            StackFrame[] stackFrames = stackTrace.GetFrames();

            if (stackFrames != null)
            {
                int count = 0;

                foreach (StackFrame fr in stackFrames)
                {
                    if (fr.GetMethod().Name.Equals("EndTracing") || fr.GetMethod().Name.Equals("StartMethodTracing") || fr.GetMethod().Name.Equals("StartActivityTracing"))
                    {
                        break;
                    }

                    count++;
                }

                MethodBase methodBase = stackFrames[count + 1].GetMethod();
                return string.Concat(methodBase.ReflectedType.FullName, ".", methodBase.Name);
            }

            return null;
        }

        #endregion Methods
    }
}